﻿// 
// Copyright (c) 2013 Jason Bell
// 
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included 
// in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
// 

using System;

using Gharen.GLCore;

namespace ExampleDraw
{
    class Program
    {
        static void Main(string[] args)
        {
            int elapsedTime = 0;
            int frameCount = 0;
            int frameTime = 0;

            var platform = Platform.Create();
            Console.WriteLine("Supported display modes:");
            foreach (DisplayMode dm in platform.DisplayModes)
                Console.WriteLine(dm);

            GLContext<GLES30> context = null;
            try
            {
                context = GLContext<GLES30>.Create();
            }
            catch (InitialisationFailedException e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
                return;
            }
            
            context.Window.Text = "Draw Example";
            
            var gl = context.GL;
            
            Console.WriteLine("OpenGL version: " + context.Version);

            //context.ChangeDisplayMode(new DisplayMode(800, 600, 60), true);

            var boxProgram = new BoxProgram(gl);

            var shadowProgram = new ShadowProgram(gl);

            var box = new Box(gl, 2, 2, 2);

            var floor = new Box(gl, 40, 0.1f, 40);

            ShadowMap shadowMap = new ShadowMap(gl);

            float angle = 0;

            gl.Enable(gl.GL_DEPTH_TEST);

            gl.ClearColor(0, 0, 1, 1);

            var stopWatch = new System.Diagnostics.Stopwatch();
            stopWatch.Start();

            while (context.Window.IsCreated)
            {
                angle += (0.001f * elapsedTime);

                shadowMap.Bind();

                gl.Viewport(0, 0, 1024, 1024);

                gl.Clear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);

                Matrix depthProj = Matrix.CreateOrthographic(-10, 10, -10, 10, -10, 20);
                Matrix depthView = Matrix.CreateLookAt(new Vector3(5, 5, 0), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
                Matrix world = Matrix.CreateIdentity() * Matrix.CreateRotation(angle, new Vector3(3, -2, 4));
                Matrix normal = Matrix.Transpose(Matrix.Inverse(world));

                shadowProgram.Bind();
                shadowProgram.Projection = depthProj;
                shadowProgram.View = depthView;
                shadowProgram.World = world;

                box.Render(0);

                shadowProgram.World = Matrix.CreateIdentity() * Matrix.CreateTranslation(0, -4, 0);

                floor.Render(0);

                shadowMap.Unbind();

                gl.Clear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);

                gl.Viewport(0, 0, context.DisplayMode.Width, context.DisplayMode.Height);

                var proj = Matrix.CreatePerspective(0.7f, (float)context.DisplayMode.Width / (float)context.DisplayMode.Height, 1.0f, 1000.0f);
                var view = Matrix.CreateLookAt(new Vector3(0, 8, 8), new Vector3(0, 0, 0), new Vector3(0, 1, 0));

                boxProgram.Bind();
                boxProgram.TextureEnabled = true;
                boxProgram.PVWMatrix = proj * view * world;
                boxProgram.NormalMatrix = normal;

                Matrix biasMatrix = new Matrix(0.5f, 0.0f, 0.0f, 0.0f,
                    0.0f, 0.5f, 0.0f, 0.0f,
                    0.0f, 0.0f, 0.5f, 0.0f,
                    0.5f, 0.5f, 0.5f, 1.0f);
                boxProgram.DepthBias = biasMatrix * depthProj * depthView * world;

                box.Render(shadowMap.depthTexture);

                boxProgram.PVWMatrix = proj * view * Matrix.CreateIdentity() * Matrix.CreateTranslation(0, -4, 0);
                
                boxProgram.NormalMatrix = Matrix.CreateIdentity();
                boxProgram.DepthBias = biasMatrix * depthProj * depthView * Matrix.CreateIdentity() * Matrix.CreateTranslation(0, -4, 0); ;

                floor.Render(shadowMap.depthTexture);

                context.SwapBuffers();

                context.Window.DoEvents();

                elapsedTime = (int)stopWatch.ElapsedMilliseconds;
                stopWatch.Restart();

                frameTime += elapsedTime;
                frameCount++;
                if (frameTime >= 1000)
                {
                    frameTime = 0;
                    context.Window.Text = "Draw Example " + frameCount;
                    frameCount = 0;
                }
            }

            shadowMap.Dispose();
            floor.Dispose();
            box.Dispose();
            shadowProgram.Dispose();
            boxProgram.Dispose();
            context.Dispose();
        }
    }
}
